package ru.niir.goodfil.db.handlers;

import org.hibernate.StatelessSession;
import ru.niir.goodfil.db.Client;
import ru.niir.goodfil.db.ClientGroup;
import ru.niir.goodfil.exceptions.InvalidIdException;
import ru.niir.goodfil.exceptions.WrongHandlerException;
import ru.niir.goodfil.utils.validation.RemoveCheckResult;
import ru.niir.goodfil.utils.validation.ValidationError;
import ru.niir.goodfil.utils.validation.ValidationProblem;
import ru.niir.goodfil.utils.validation.ValidationResult;

import static org.apache.commons.lang.StringUtils.isBlank;
import static ru.niir.goodfil.utils.Utils._assert;

/**
 * Обработчик для клиентских групп.
 *
 * @see ru.niir.goodfil.db.ClientGroup клиентские группы
 */
public class ClientGroupHandler extends Handler
{
    /**
     * Конструктор
     */
    public ClientGroupHandler(Goodfil gf)
    {
        super(ClientGroup.class, gf);
    }

    /**
     * Проверки для клиентских групп: 1. name заполнено 2. criteriaSum > 0 (поле unsigned) 3. name уникально В ПРЕДЕЛАХ
     * БРЭНДА (индекс: IdBrandAndNameUnique) 4. criteriaSum уникально В ПРЕДЕЛАХ БРЭНДА (индекс:
     * IdBrandAndCriteriaSumUnique)
     *
     * @param o клиент
     * @return результат проверки
     */
    @Override
    public ValidationResult validate(Object o)
    {
        assert o != null;
        if (!(o instanceof ClientGroup)) throw new WrongHandlerException(klass, o.getClass());

        ClientGroup clientGroup = (ClientGroup) o;
        ValidationResult result = ValidationResult.Ok(ClientGroup.class);

        if (isBlank(clientGroup.getName()))
        {
            result.addValidationError(new ValidationError("name", "Наименование", clientGroup.getName(), ValidationProblem.NOT_EMPTY));
        }
        if (clientGroup.getCriteriaSum() <= 0)
        {
            result.addValidationError(new ValidationError("criteriaSum", "Макс. сума закупок за предыдущий месяц", clientGroup.getCriteriaSum(), ValidationProblem.POSITIVE_ZERO));
        }
        if (!checkClientGroupNameIsUnique(clientGroup.getBrandId(), clientGroup.getName(), clientGroup.getId()))
        {
            result.addValidationError(new ValidationError("name", "Наименование", clientGroup.getName(), ValidationProblem.UNIQUE));
        }
        if (!checkClientGroupCriteriaSumIsUnique(clientGroup.getBrandId(), clientGroup.getCriteriaSum(), clientGroup.getId()))
        {
            result.addValidationError(new ValidationError("criteriaSum", "Мин. сума закупок за предыдущий месяц", clientGroup.getCriteriaSum(), ValidationProblem.UNIQUE));
        }

        return result;
    }

    @Override
    public void postSave(Object o)
    {
        assert o != null;
        if (!(o instanceof ClientGroup)) throw new WrongHandlerException(klass, o.getClass());

        //  Создание цен при создании клиентской группы
        //  Это теперь обрабатывается на стороне БД за счет триггера PostAddClientGroup
//        ClientGroup clientGroup = (ClientGroup) o;
//        getSession().createQuery("delete from SimplePricelist where clientGroupId = ?").setLong(0, clientGroup.getId()).executeUpdate();
//
//        long count = (Long) getSession().createQuery("select count(*) from Good where isDeleted=false").uniqueResult();
//        for (int beg = 0; beg < count; beg += 100)
//        {
//            List result = getSession().createQuery("select id from Good where isDeleted=false").setFirstResult(beg).setMaxResults(100).list();
//            for (Object aResult : result)
//            {
//                long goodId = (Long) aResult;
//                getSession().insert(new SimplePricelist(goodId, clientGroup.getId(), 0));
//            }
//        }
    }

    /**
     * При удалении клиентской группы из прайслиста удаляются записи для этой клиентской группы
     *
     * @param clientGroupId идентификатор клиентской группы
     */
    @Override
    public void preRemove(long clientGroupId)
    {
        assert clientGroupId >= 0;

        //  Удаление цен при удалении клиентской группы
        //  Это теперь обрабатывается на стороне БД за счет ключей на таблицу SimplePricelist
//        getSession().createQuery("delete from SimplePricelist where clientGroupId = ?").setLong(0, clientGroupId).executeUpdate();
    }

    /**
     * Перед удалением клиентской группы проверяется, что в ней нет клиентов
     *
     * @param clientGroupId идентификатор клиентской группы
     * @return результат проверки
     * @throws InvalidIdException если клиентской группы с указанным идентификатором не существует
     */
    @Override
    public RemoveCheckResult removeCheck(long clientGroupId) throws InvalidIdException
    {
        _assert(clientGroupId >= 0);

        if (!exists(ClientGroup.class, clientGroupId)) throw new InvalidIdException(ClientGroup.class, clientGroupId);

        if (isClientGroupIsBusy(clientGroupId)) return RemoveCheckResult.Error(ClientGroup.class, Client.class);
        return RemoveCheckResult.Ok(ClientGroup.class);

    }

    /**
     * Возвращает true, если к клиентской группе отнесен хотя бы один клиент
     */
    public boolean isClientGroupIsBusy(long clientGroupId)
    {
        StatelessSession ss = gf.getSession();
        return ((Number) ss.createSQLQuery("select count(*) from ClientsClientgroups where IdClientGroup=?").setLong(0, clientGroupId).uniqueResult()).longValue() > 0;
    }

    /**
     * Возвращает true, если клиентской группы с указаным именем не существует. Проверка для функции создания клиентской
     * группы В ПРЕДЕЛАХ БРЭНДА.
     *
     * @param name имя клиентской группы
     * @return true, если клиентской группы с указаным именем не существует
     */
    public boolean checkClientGroupNameIsUnique(long brandId, String name)
    {
        return checkClientGroupNameIsUnique(brandId, name, -1);
    }

    /**
     * Возвращает true, если клиентской группы с указаным именем не существует, КРОМЕ клиентской группы с данным
     * идентификатором В ПРЕДЕЛАХ БРЭНДА. Проверка для функции редактирования клиентской группы.
     *
     * @param name имя клиентской группы
     * @param id   идентификатор клиентской группы
     * @return true, если клиентской группы с указаным именем не существует
     */
    public boolean checkClientGroupNameIsUnique(long brandId, String name, long id)
    {
        return ((Long) getSession().createQuery("select count(*) from ClientGroup cg where name = ? and id != ? and brandId = ?")
                .setString(0, name)
                .setLong(1, id)
                .setLong(2, brandId)
                .uniqueResult() == 0);
    }

    /**
     * Возвращает true, если клиентской группы с указанной суммой заккупок не существует. Проверка для функции создания
     * клиентской группы В ПРЕДЕЛАХ БРЭНДА.
     *
     * @param criteriaSum сумма заккупок клиентской группы
     * @return true, если клиентской группы с указанной суммой заккупок не существует
     */
    public boolean checkClientGroupCriteriaSumIsUnique(long brandId, float criteriaSum)
    {
        return checkClientGroupCriteriaSumIsUnique(brandId, criteriaSum, -1);
    }

    /**
     * Возвращает true, если клиентской группы с указанной суммой закупок не существует, КРОМЕ клиентской группы с
     * данным идентификатором В ПРЕДЕЛАХ БРЭНДА. Проверка для функции редактирования клиентской группы.
     *
     * @param criteriaSum сумма заккупок клиентской группы
     * @return true, если клиентской группы с указанной суммой заккупок не существует
     */
    public boolean checkClientGroupCriteriaSumIsUnique(long brandId, float criteriaSum, long id)
    {
        return ((Long) getSession().createQuery("select count(*) from ClientGroup cg where criteriaSum = ? and id != ? and brandId = ?")
                .setFloat(0, criteriaSum)
                .setLong(1, id)
                .setLong(2, brandId)
                .uniqueResult() == 0);
    }
}
